package net.ncraft.mc.easyframework.mvc.command;

import net.ncraft.mc.easyframework.context.ApplicationContext;
import net.ncraft.mc.easyframework.mvc.anno.*;
import org.bukkit.command.CommandSender;
import org.bukkit.util.StringUtil;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

public class CommandInterceptor implements InvocationHandler {

    private final ApplicationContext context;
    private final Map<Class<?>, List<Method>> METHODS;
    private final Map<Class<?>, String> ROOTS_SIGNS;

    public CommandInterceptor(ApplicationContext context, Map<Class<?>, List<Method>> methods, Map<Class<?>, String> rootsSigns) {
        this.context = context;
        METHODS = methods;
        ROOTS_SIGNS = rootsSigns;
    }

    @Override
    public Object invoke(Object o, Method method, Object[] inputArgs) throws Throwable {

        //CommandSender commandSender, org.bukkit.command.Command command, String s, String[] strings
        if (method.getName().equals("onCommand")) {
            for (Class<?> controller : METHODS.keySet()) {

                String rootSign = ROOTS_SIGNS.get(controller);

                for (Method m : METHODS.get(controller)) {
                    String signStr = m.getAnnotation(Command.class).value();
                    if (!rootSign.isEmpty()) {
                        signStr = rootSign + " " + signStr;
                    }
                    List<String> sign = new ArrayList<>(Arrays.stream(signStr.split(" ")).toList());
                    String[] inputArg = (String[]) inputArgs[3];
                    List<String> args = new ArrayList<>(Arrays.asList(inputArg));

                    if (sign.contains("...")) {
                        if (args.size()>=sign.size()) {
                            for (int i = sign.indexOf("..."); i < args.size(); i++) {
                                sign.add("{}");
                            }
                        }
                        sign.remove("...");
                    }

                    if (sign.size() != args.size()) {
                        continue;
                    }
                    List<String> argsName = new ArrayList<>();

                    //去除{} 拿到里面的内容 sign里的{xxx}都替换成{}
                    for (int i = 0; i < sign.size(); i++) {
                        if (sign.get(i).contains("{")) {
                            argsName.add(sign.get(i).replace("{", "").replace("}", ""));
                            sign.set(i, "{}");
                        } else {
                            argsName.add("{NONE}");
                        }

                    }
                    StringBuilder signBuilder = new StringBuilder();

                    //拼接成完整指令 参数之间要空格
                    for (int i = 0; i < sign.size(); i++) {
                        if (sign.get(i).equals("{}")) {
                            signBuilder.append(args.get(i)).append(" ");
                        } else {
                            signBuilder.append(sign.get(i)).append(" ");
                        }
                    }

                    //玩家输入的指令拼接
                    StringBuilder inputArgsBuilder = new StringBuilder();
                    for (Object arg : args) {
                        if (arg instanceof CommandSender) {
                            continue;
                        }
                        inputArgsBuilder.append(arg).append(" ");
                    }

                    if (!signBuilder.toString().trim().equalsIgnoreCase(inputArgsBuilder.toString().trim())) {
                        continue;
                    }

                    Object[] invokeArgs = new Object[m.getParameterCount()];

                    //拿到m方法的形参
                    for (int i = 0; i < m.getParameters().length; i++) {

                        Parameter parameter = m.getParameters()[i];

                        if (parameter.isAnnotationPresent(OriginalParam.class)) {
                            //判断是否是 String[]类型
                            if (parameter.getType().equals(String[].class)) {
                                invokeArgs[i] = inputArg;
                                continue;
                            }
                        }


                        //是否被sender注解修饰
                        if (parameter.isAnnotationPresent(Sender.class)) {
                            //是否是commandSender类型
                            if (parameter.getType().equals(CommandSender.class)) {
                                invokeArgs[i] = inputArgs[0];
                                continue;
                            }
                        }

                        //如果没有被Param修饰 那么判断字段名是否相对 赋值
                        for (int j = 0; j < argsName.size(); j++) {
                            if (parameter.getName().equals(argsName.get(j))) {
                                invokeArgs[i] = args.get(j);
                            }
                        }

                        //如果parameter被ParamBody修饰
                        if (parameter.isAnnotationPresent(ParamBody.class)) {
                            //拿到当前形参对象里的字段 实例化并赋值
                            Class<?> clazz = parameter.getType();
                            Object obj = clazz.getConstructor().newInstance();
                            for (int j = 0; j < argsName.size(); j++) {

                                Field field = null;
                                try {
                                    field = clazz.getDeclaredField(argsName.get(j));
                                } catch (NoSuchFieldException e) {
                                    continue;
                                }

                                field.setAccessible(true);

                                //类型转换
                                field.set(obj, args.get(j));

                            }
                            //放入对应槽位
                            invokeArgs[i] = obj;
                        }

                        //如果parameter被Param修饰
                        if (parameter.isAnnotationPresent(Param.class)) {
                            //拿到param注解的value
                            String v = parameter.getAnnotation(Param.class).value();
                            for (int k = 0; k < argsName.size(); k++) {
                                if (argsName.get(k).equals(v)) {
                                    invokeArgs[i] = args.get(k);
                                    break;
                                }
                            }
                        }

                    }

                    if (m.isAnnotationPresent(Permission.class)) {
                        Permission permissionAnno = m.getAnnotation(Permission.class);

                        CommandSender sender = (CommandSender) inputArgs[0];
                        String permission = permissionAnno.value();
                        String message = permissionAnno.message().replace("&", "§");

                        if (!permission.isEmpty()) {
                            if (!sender.hasPermission(permission)) {
                                sender.sendMessage(message);
                                return true;
                            }
                        }
                    }

                    m.invoke(context.getBean(controller), invokeArgs);
                    return true;
                }
            }

            return true;

        }

        throw new RuntimeException("Only support onCommand method");
    }
}
